<?php
namespace app\service;

use app\service\cache\UserDeviceBindingCacheService;
use share\exception\ApiException;
use share\exception\Errno;
use share\service\UserBaseService;
use share\service\UserDetailService;
use share\utils\Helper;
use think\Log;

/**
 * 阿里云与本地数据同步相关接口
 */
class AliyunProxyService{

    const DEFAULT_PROJECT_ID = 'a123yDt0dd5TecR9';//ENZ
    const DEFAULT_REGION = 'CN';

    const SHARE_INFO_KEEP_ALIVE_TIME = 1800; // 半小时内添加的分享数据不删除

    /**
     * @return \app\service\AliyunService
     */
    public static function getAliyunService($param = []){
        $projectId = isset($param['projectId']) ? $param['projectId'] : self::DEFAULT_PROJECT_ID;
        $region = isset($param['region']) ? $param['region'] : self::DEFAULT_REGION;
        return AliyunService::instance($projectId,$region);
    }

    
    /**
     * 同步设备的绑定关系
     * @param array $param
     * @return null
     */
    public static function asyncDeviceBindInfoByDeviceName($param){

        trace("asyncDeviceBindInfoByDeviceName ".json_encode($param), Log::ERROR);

        $deviceName = isset($param['deviceName']) ? $param['deviceName'] :'' ;
        $productKey = isset($param['productKey']) ? $param['productKey'] :'' ;
 
        $vendor = isset($param['vendor']) ? $param['vendor'] :'' ;
        if(empty($deviceName)){
            throw new ApiException("param deviceName is required",Errno::COMMON_INVALID_INPUT);
        }
        if($vendor){
            $projectId = ProjectInfoService::getProjectIdByVendor($vendor);
            if(empty($projectId)){
                trace("unknown projectId for vendor ".$vendor,Log::ERROR);
                return;
            }
            else{
                $param['projectId'] = $projectId;
            }            
        }
        
        $deviceInfo = DeviceInfoTableService::getBase($deviceName);
        if(!empty($deviceInfo)){
            return self::asyncDeviceBindInfoByIotId([
                'iotId' => $deviceInfo['iot_id'],
                'region' => $deviceInfo['server_site'],
                'vendor' => $vendor,
            ]);
        }
        // 本地不存在设备信息
        $aliyunService = self::getAliyunService();
        $regionInfo = $aliyunService->getRawThingRegoin($productKey,$deviceName);
        $region = $aliyunService::getRegionByLocation($regionInfo);
        $aliyunService->setRegion($region);
        $remoteDeviceInfo = $aliyunService->getRawThingInfo('',$productKey,$deviceName);
        if($remoteDeviceInfo && isset($remoteDeviceInfo['iotId']) && !empty($remoteDeviceInfo['iotId'])) {
            $tn = time();
            $addItem = [
                'device_name' => $deviceName,
                'iot_id' => $remoteDeviceInfo['iotId'],
                'ch_num' => 1,
                'extend_num' => 0,
                'server_site' => $region,
                'vendor' => $vendor,
                'firsttime' => $tn,
                'modifytime' => $tn,
            ];
            $updateItem = [
                'iot_id' => $remoteDeviceInfo['iotId'],
                'modifytime' => $tn,
            ];
            DeviceInfoTableService::upsert($addItem,$updateItem);
        }
        return self::asyncDeviceBindInfoByIotId([
            'iotId' => $remoteDeviceInfo['iotId'],
            'region' => $region,
            'vendor' => $vendor,
        ]);
    } 

    /**
     * 同步设备的绑定关系
     * @param array $param
     */
    public static function asyncDeviceBindInfoByIotId($param){

        // trace("asyncDeviceBindInfoByIotId ".json_encode($param), Log::ERROR);
        $iotId = isset($param['iotId']) ? $param['iotId'] :'' ;
        if(empty($iotId)){
            throw new ApiException("param iotId is required",Errno::COMMON_INVALID_INPUT);
        }
        $region = isset($param['region']) ? $param['region'] :'' ;
        $vendor = isset($param['vendor']) ? $param['vendor'] :'' ;
        if(empty($region)){
            $deviceInfo = DeviceInfoTableService::getByFilter([
                'iot_id' => $iotId,
            ]);
            if(!empty($deviceInfo)){
                $param['region'] = $deviceInfo['server_site'];
            }
        }
        if($vendor){
            $projectId = ProjectInfoService::getProjectIdByVendor($vendor);
            if(empty($projectId)){
                trace("unknown projectId for vendor ".$vendor,Log::ERROR);
                return;
            }
            else{
                $param['projectId'] = $projectId;
            }            
        }
        $aliyunService = self::getAliyunService($param);
        //{"accountDevList":[{"iotId":"jTAUbuWc1ku7MbyZ6VdK000000","nickName":"10.8.0.8","owned":1,"identityId":"50a6opa8b4ad03922a0eccb6cb66a71c0e456549","productKey":"a1dYnwitxHF","deviceName":"jTAUbuWc1ku7MbyZ6VdK","productName":"4G-AI-IPCAM"},{"iotId":"jTAUbuWc1ku7MbyZ6VdK000000","nickName":"\u697c\u9053","owned":0,"identityId":"5004opd03db0fcb1c08209e1de84b26f010a5296","productKey":"a1dYnwitxHF","deviceName":"jTAUbuWc1ku7MbyZ6VdK","productName":"4G-AI-IPCAM"}],"total":2}
        $bindDeviceInfo = $aliyunService->getRawUserGetBindDevice($iotId);
        if($bindDeviceInfo && isset($bindDeviceInfo['accountDevList'])){ // 同步绑定信息与分享信息
            @set_time_limit(0); //不限制超时
            if($bindDeviceInfo['total'] >= 200){
                // TODO  翻页获取
                trace("设备 iotId:".$iotId." 的分享信息条数已超过 200. 同步信息出错",Log::ERROR);
                return ;
            }
            $identityIds = Helper::arraySmartUnique(array_column($bindDeviceInfo['accountDevList'],'identityId'));
            $userInfos = UserBaseService::getAllByFilter([
                'identity_id' => ['in', $identityIds],
            ]);
            $shareAdmin = null;
            $bindDevice = [];
            $shareDevices = [];
            $tn = time();
            $tnMs = Helper::getMSTime();
            $oldBindInfo = UserDeviceBindingService::getByFilter(['iot_id' => $iotId]);
            $oldShareInfos = UserDeviceShareService::getAllByFilter(['iot_id' => $iotId]);
            foreach($bindDeviceInfo['accountDevList'] as $bindInfo){
                if($bindInfo['owned'] == 1){
                    $shareAdmin = Helper::filterOne($userInfos,['identity_id' => $bindInfo['identityId']]);
                    if(empty($shareAdmin)){
                        trace("device iotId:".$iotId." bindUser identityId:". $bindInfo['identityId']." not exists");
                        return;
                    }
                    $bindDevice = [
                        'iot_id' => $iotId,
                        'uid' => $shareAdmin['id'],
                        'vendor' => empty($shareAdmin['vendor']) ? $vendor :$shareAdmin['vendor'],
                        'amqptime' => $tnMs,
                        'firsttime' => $tn,
                        'modifytime' => $tn,
                    ];
                    // 删除绑定用户的缓存
                    UserDeviceBindingCacheService::del(['uid' => $shareAdmin['id']]);
                }
                else{
                    $shareUser = Helper::filterOne($userInfos,['identity_id' => $bindInfo['identityId']]);
                    if(empty($shareUser)){
                        trace("device iotId:".$iotId." shareUser identityId:". $bindInfo['identityId']." not exists");
                        continue;
                    }
                    $shareDevices[] = [
                        'iot_id' => $iotId,
                        'share_uid' => $shareUser['id'],
                        'rule' => '',
                        'main_id' => $iotId,
                        'vendor' => empty($shareUser['vendor']) ? $vendor :$shareUser['vendor'],
                        'amqptime' => $tnMs,
                        'firsttime' => $tn,
                        'modifytime' => $tn,
                    ];     
                }
            }
            if(empty($bindDevice)){
                return ;
            }

            if(empty($oldBindInfo)){ //原始绑定信息不存在
                UserDeviceBindingService::add($bindDevice);
            }
            else{
                if($oldBindInfo['uid'] != $bindDevice['uid']){ //原始绑定用户已变更
                    UserDeviceBindingService::update($oldBindInfo['id'],[
                        'uid' => $bindDevice['uid'],
                        'vendor' => $bindDevice['vendor'],
                        'amqptime' => $bindDevice['amqptime'],
                        'firsttime' => $bindDevice['firsttime'],
                        'modifytime' => $bindDevice['modifytime'],
                    ]);
                }
            }

            $shareDel = [];
            foreach($oldShareInfos as $shareDeviceItem){
                if($shareDeviceItem['admin_uid'] !=  $shareAdmin['id']){  //绑定者已变更
                    $shareDel[] = $shareDeviceItem['id'];
                    continue;
                }
                $existShareInfo = Helper::filterOne($shareDevices,['share_uid' => $shareDeviceItem['share_uid']]);
                if(empty($existShareInfo)){ // 不存在该分享者
                    if($tn - $shareDeviceItem['firsttime'] > self::SHARE_INFO_KEEP_ALIVE_TIME && 
                        $tn - $shareDeviceItem['modifytime'] > self::SHARE_INFO_KEEP_ALIVE_TIME &&
                        $tnMs - $shareDeviceItem['amqptime'] > self::SHARE_INFO_KEEP_ALIVE_TIME * 1000
                    ){
                        $shareDel[] = $shareDeviceItem['id'];
                    }
                }
            }

            if($shareDel){
                trace("shareUserNotExist iotId:{$iotId} shareId:".json_encode($shareDel));
                // UserDeviceShareService::delByFilter(['id' => [
                //     'in',$shareDel
                // ]]);
            }
            // if($shareAdd){
            //     UserDeviceShareService::addBulk($shareAdd);
            // }
        }
    }

    public static function getUserAllDeviceInfo($uid){
        $userInfo = UserBaseService::getBase($uid);
        if(empty($userInfo)){ //
            throw new ApiException("userInfo not exist when asyncUserBindInfo. uid ".$uid,Errno::COMMON_INVALID_INPUT);
        }
        if(empty($userInfo['identity_id'])){
            trace("unknown identity_id for user_detail. id ".$uid ,Log::ERROR);
            return;
        }
        $userDetail = UserDetailService::getBase($uid);
        if(empty($userDetail)){          
            return;
        }
        if(empty($userDetail)){
            trace("user_detail empty. id ".$uid ,Log::ERROR);
            return;
        }
        if(empty($userDetail['server_site'])){
            trace("user_detail server_site is empty. id ".$uid ,Log::ERROR);
            return;
        }
        $site  = $userDetail['server_site'];
        $vendor = $userInfo['vendor'];
        $projectId = ProjectInfoService::getProjectIdByVendor($vendor);
        if(empty($projectId)){
            trace("unknown projectId for vendor ".$vendor,Log::ERROR);
            return;
        }
        $aliyunParam = [
            'projectId' =>  $projectId,
            'region' => $site,
        ];
        $aliyunService = self::getAliyunService($aliyunParam);
        $allBindInfo = [];
        $offset = 0;
        @set_time_limit(0);
        do{
            $resp = $aliyunService->getUserDevice($userInfo['identity_id'],'','',100,$offset);
            $bindInfo = [];
            if(isset($resp) && $resp['code'] == 200){
                $bindInfo = $resp['data'];
            }
            else{
                // 获取出错  直接返回
                return;
            }
            $offset += 100;
            if(is_array($bindInfo)){
                $allBindInfo = array_merge($allBindInfo,$bindInfo);
                if(count($bindInfo) < 100){
                    break;
                }
            }
            else{
                break;
            }
        }while(true);
        return $allBindInfo; 
    }

    /**
     * 同步用户的设备信息
     * @param array $param
     */
    public static function asyncUserBindInfo($param){

        trace("asyncUserBindInfo ".json_encode($param), Log::ERROR);
        $uid = $param['uid'];
        $userInfo = UserBaseService::getBase($uid);
        if(empty($userInfo)){ //
            throw new ApiException("userInfo not exist when asyncUserBindInfo. uid ".$uid,Errno::COMMON_INVALID_INPUT);
        }
        if(empty($userInfo['identity_id'])){
            trace("unknown identity_id for user. id ".$uid ,Log::ERROR);
            return;
        }
        $userDetail = UserDetailService::getBase($uid);
        if(empty($userDetail)){
            trace("user_detail empty. id ".$uid ,Log::ERROR);
            return;
        }
        if(!isset($param['region'])){
            if(empty($userDetail['server_site'])){
                trace("user server_site is empty identity_id:".$userInfo['identity_id']." . id: ".$uid ,Log::ERROR);
                $param['region'] = 'CN'; //默认为中国
            }
            else{
                $param['region'] = $userDetail['server_site'];
            }            
        }

        $vendor = isset($param['vendor']) ? $param['vendor'] : $userInfo['vendor'] ;
        if($vendor){
            $projectId = ProjectInfoService::getProjectIdByVendor($vendor);
            if(empty($projectId)){
                trace("unknown projectId for vendor ".$vendor,Log::ERROR);
                return;
            }
            else{
                $param['projectId'] = $projectId;
            }            
        }
        
        $aliyunService = self::getAliyunService($param);
        $allBindInfo = [];
        $offset = 0;
        @set_time_limit(0);
        do{
            $resp = $aliyunService->getUserDevice($userInfo['identity_id'],'','',100,$offset);
            $bindInfo = [];
            if(isset($resp) && $resp['code'] == 200){
                $bindInfo = $resp['data'];
            }
            else{
                // 获取出错  直接返回
                return;
            }
            $offset += 100;
            if(is_array($bindInfo)){
                $allBindInfo = array_merge($allBindInfo,$bindInfo);
                if(count($bindInfo) < 100){
                    break;
                }
            }
            else{
                break;
            }
        }while(true);

        $allBindInfo = Helper::filter($allBindInfo,['owned' => 1]);

        if(empty($allBindInfo)){ //可能获取出错
            return;
        }

        $relationIotIds = Helper::arraySmartUnique(array_column($allBindInfo,'iotId'));

        $bindInfos = UserDeviceBindingService::getAllByFilter(['iot_id' =>  ['in',$relationIotIds]]);

     
        foreach($allBindInfo as $remoteBind){
            $iotId = $remoteBind['iotId'];           
            $currentBind = Helper::filterOne($bindInfos,[
                'iot_id' => $iotId,
            ]);
            if($currentBind){ //如果存在
                if($currentBind['uid'] !== $userInfo['id']){ //绑定用户已变更
                    trace("bindUserChanged:".json_encode($currentBind), Log::ERROR);

                    // UserDeviceShareService::delByFilter([
                    //     'iot_id' => $iotId,
                    //     'admin_uid' => $userInfo['id'],
                    // ]);
                    // UserDeviceBindingService::del($currentBind['id']);
                }
            }
        } 
    }

    /**
     * 强制解除设备上所有的绑定信息
     */
    public static function unbindAllUser($param){

        trace("unbindAllUser ".json_encode($param), Log::ERROR);

        $deviceName = isset($param['deviceName']) ? $param['deviceName'] :'' ;
        $productKey = isset($param['productKey']) ? $param['productKey'] :'' ;
        $iotId = isset($param['iotId']) ? $param['iotId'] :'' ;

        if(empty($deviceName) && empty($iotId)){
            trace("unbind device but device info [deviceName,iotId] both empty ".json_encode($param),Log::ERROR);
            return;
        }

        if($deviceName){
            $deviceInfo = DeviceInfoTableService::getBase($deviceName);
        }
        else{
            $deviceInfo = DeviceInfoTableService::getByIotId($iotId);
        }
        if(empty($deviceInfo)){
            trace("can not find the deviceInfo by param ".json_encode($param),Log::ERROR);
            return;
        }

        $vendor = isset($param['vendor']) ? $param['vendor'] : $deviceInfo['vendor'];
        if(!isset($param['region'])){
            $param['region'] = $deviceInfo['server_site'];
        }

        if($vendor){
            $projectId = ProjectInfoService::getProjectIdByVendor($vendor);
            if(empty($projectId)){
                trace("unknown projectId for vendor ".$vendor,Log::ERROR);
                return;
            }
            else{
                $param['projectId'] = $projectId;
            }            
        } 
        $aliyunService = self::getAliyunService($param);
        $aliyunService->getRawUserDeviceUnbindAll($iotId,$productKey,$deviceName);
    }

}